//
//  GLProgram.m
//  whfDemo
//
//  Created by 王洪飞 on 2024/2/20.
//

#import "GLProgram.h"


@implementation GLProgram


- (instancetype)initWithVertexShaderString:(NSString *)vShaderString
                      fragmentShaderString:(NSString *)fShaderString
{
    self = [super init];
    if (self) {
        _initialized = NO;
        attributes = [[NSMutableArray alloc] init];
        uniforms = [[NSMutableArray alloc] init];
        program = glCreateProgram();
        
        if(![self compileShader:&vertShader type:GL_VERTEX_SHADER string:vShaderString]){
            NSLog(@"failed to compile vertex shader = %@", self.vertexShaderLog);
        }
        if(![self compileShader:&fragShader type:GL_FRAGMENT_SHADER string:fShaderString]){
            NSLog(@"failed to compile fragment shader == %@", self.fragmentShaderLog);
        }
        glAttachShader(program, vertShader);
        glAttachShader(program, fragShader);
    }
    return self;
}

// END:init
// START:compile
- (BOOL)compileShader:(GLuint *)shader
                 type:(GLenum)type
               string:(NSString *)shaderString{
    
    GLint status;
    const GLchar *source;
    source = (GLchar *)[shaderString UTF8String];
    if(!source){
        NSLog(@"failed to load vertex shader");
        return NO;
    }
    
    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);
    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
    NSLog(@"shader source ===%d : %@", status, shaderString);
    if(status != GL_TRUE){
        GLint logLength;
        glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
        if(logLength > 0){
            GLchar *log = (GLchar *)malloc(logLength);
            glGetShaderInfoLog(*shader, logLength, NULL, log);
            if (shader == &vertShader) {
                self.vertexShaderLog = [NSString stringWithFormat:@"%s", log];
                NSLog(@"log %s", log);
            } else {
                self.fragmentShaderLog = [NSString stringWithFormat:@"%s", log];
                NSLog(@"log %s", log);
            }
            free(log);
        }
        return NO;
    }
    NSLog(@"compile shader success");
    return YES;
}


- (void)addAttribute:(NSString *)attributeName{
    if(![attributes containsObject:attributeName]){
        [attributes addObject:attributeName];
        glBindAttribLocation(program, (GLuint)[attributes indexOfObject:attributeName], attributeName.UTF8String);
    }
}

- (GLuint)attributeIndex:(NSString *)attributeName{
    return glGetAttribLocation(program, attributeName.UTF8String);
//    return (GLuint)[attributes indexOfObject:attributeName];
}

- (GLuint)uniformIndex:(NSString *)uniformName{
    return glGetUniformLocation(program, [uniformName UTF8String]);
}

- (BOOL)link{
    GLint status;
    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &status);
    
    if(status == GL_FALSE){
        return NO;
    }
    if(vertShader){
        glDeleteShader(vertShader);
        vertShader = 0;
    }
    if(fragShader){
        glDeleteShader(fragShader);
        fragShader = 0;
    }
    self.initialized = YES;
    return YES;
}

-(void)use{
    glUseProgram(program);
}

- (void)validate{
    GLint logLength;
    glValidateProgram(program);
    glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(program, logLength, &logLength, log);
        self.programLog = [NSString stringWithFormat:@"%s", log];
        free(log);
    }
}

- (void)dealloc{
    if (vertShader)
        glDeleteShader(vertShader);
        
    if (fragShader)
        glDeleteShader(fragShader);
    
    if (program)
        glDeleteProgram(program);
       
}

@end
